#include "CameraController.h"

CameraController::CameraController(osg::Camera* camera, float ratio) :
camera(camera),
minZoomLevel(0.1f),
maxZoomLevel(10.0f),
zoomLevel(1.0f),
ratio(ratio),
sceneHWidth(10.0f)
{

}

void CameraController::changeZoom(float dz)
{
	zoomLevel += dz * 0.1f;
	if (zoomLevel < minZoomLevel)
	{
		zoomLevel = minZoomLevel;
	}
	else if (zoomLevel > maxZoomLevel)
	{
		zoomLevel = maxZoomLevel;
	}
}


/*
**
** Perspective
**
*/

CControllerPerspective::CControllerPerspective(osg::Camera *camera, float ratio) : CameraController(camera, ratio), ax(-45.0f), ay(30.0f), x(0), y(0)
{
	camera->setProjectionMatrixAsPerspective(30.0f, ratio, 1.0f, 10000.0f);
	updateViewMatrix();
}

void CControllerPerspective::resize(float ratio)
{
	camera->setProjectionMatrixAsPerspective(30.0f, ratio, 1.0f, 10000.0f);
	this->ratio = ratio;
}

void CControllerPerspective::zoom(float dz)
{
	changeZoom(dz);
	updateViewMatrix();
}

void CControllerPerspective::move(float dx, float dy)
{
	x += dx * 0.2f;
	y += dy * 0.2f;
	updateViewMatrix();
}

void CControllerPerspective::rotate(float dx, float dy)
{
	ax += dx * 0.2f;
	ay -= dy * 0.2f;
	checkAngle(ax);
	checkAngle(ay);
	updateViewMatrix();
}

void CControllerPerspective::checkAngle(float& angle)
{
	while (angle > 360.0f)
	{
		angle -= 360.0f;
	}
	while (angle < 0.0f)
	{
		angle += 360.0f;
	}
}

void CControllerPerspective::updateViewMatrix()
{
	osg::Matrix m = osg::Matrix::identity();
	m *= osg::Matrix::rotate(osg::DegreesToRadians(ax), 0, 1, 0);
	m *= osg::Matrix::rotate(osg::DegreesToRadians(ay), 1, 0, 0);
	m *= osg::Matrix::translate(x, y, -sceneHWidth * 4.0f * zoomLevel);
	camera->setViewMatrix(m);
}



/*
**
** Isometric
**
*/

CControllerIsometric::CControllerIsometric(osg::Camera *camera, float ratio) : CameraController(camera, ratio), x(0), y(0), xpx(0.0f), ypx(0.0f)
{
	updateProjection();
	updateViewMatrix();
}

void CControllerIsometric::resize(float ratio)
{
	this->ratio = ratio;
	updateProjection();
}

void CControllerIsometric::zoom(float dz)
{
	changeZoom(dz);
	updateProjection();
}

void CControllerIsometric::move(float dx, float dy)
{
	float ddx = dx * xpx;
	float ddy = dy * ypx * 2.0f;
	x -= (ddx - ddy) * 0.7071067811865475244008443621052f;
	y -= (-ddx - ddy) * 0.7071067811865475244008443621052f;
	updateViewMatrix();
}

void CControllerIsometric::updateProjection()
{
	float py = 1.2f * sceneHWidth * zoomLevel;
	float px = py * ratio;
	camera->setProjectionMatrixAsOrtho2D(-px, px, -py, py);
	xpx = (px * 2.0f) / (float)camera->getViewport()->width();
	ypx = (py * 2.0f) / (float)camera->getViewport()->height();
}

void CControllerIsometric::updateViewMatrix()
{
	camera->setViewMatrixAsLookAt(osg::Vec3(10 + x, 8.1649658092772603273242802490196f, 10 + y), osg::Vec3(x, 0, y), osg::Vec3(0, 1, 0));
}


/*
**
** SideView
**
*/

CControllerSideView::CControllerSideView(osg::Camera* camera, float ratio) : CameraController(camera, ratio), x(0.0f), y(0.0f), xpx(0.0f), ypx(0.0f)
{
	updateProjection();
}

void CControllerSideView::resize(float ratio)
{	
	this->ratio = ratio;
	updateProjection();
}

void CControllerSideView::zoom(float dz)
{
	changeZoom(dz);
	updateProjection();
}

void CControllerSideView::move(float dx, float dy)
{
	x += dx * xpx;
	y += dy * ypx;
	updateViewMatrix();
}

void CControllerSideView::updateProjection()
{
	float py = 1.2f * sceneHWidth * zoomLevel;
	float px = py * ratio;
	camera->setProjectionMatrixAsOrtho2D(-px, px, -py, py);
	xpx = (px * 2.0f) / (float)camera->getViewport()->width();
	ypx = (py * 2.0f) / (float)camera->getViewport()->height();
}




/*
**
** Top
**
*/

CControllerTopView::CControllerTopView(osg::Camera *camera, float ratio) : CControllerSideView(camera, ratio)
{
	updateViewMatrix();
}

void CControllerTopView::updateViewMatrix()
{
	camera->setViewMatrixAsLookAt(osg::Vec3(-x, 1, y), osg::Vec3(-x, 0, y), osg::Vec3(0, 0, -1));
}



/*
**
** Bottom
**
*/

CControllerBottomView::CControllerBottomView(osg::Camera *camera, float ratio) : CControllerSideView(camera, ratio)
{
	updateViewMatrix();
}

void CControllerBottomView::updateViewMatrix()
{
	camera->setViewMatrixAsLookAt(osg::Vec3(-x, -1, -y), osg::Vec3(-x, 0, -y), osg::Vec3(0, 0, 1));
}



/*
**
** Left
**
*/

CControllerLeftView::CControllerLeftView(osg::Camera *camera, float ratio) : CControllerSideView(camera, ratio)
{
	updateViewMatrix();
}

void CControllerLeftView::updateViewMatrix()
{
	camera->setViewMatrixAsLookAt(osg::Vec3(-1, -y, -x), osg::Vec3(0, -y, -x), osg::Vec3(0, 1, 0));
}




/*
**
** Right
**
*/

CControllerRightView::CControllerRightView(osg::Camera *camera, float ratio) : CControllerSideView(camera, ratio)
{
	updateViewMatrix();
}

void CControllerRightView::updateViewMatrix()
{
	camera->setViewMatrixAsLookAt(osg::Vec3(1, -y, x), osg::Vec3(0, -y, x), osg::Vec3(0, 1, 0));
}





/*
**
** Front
**
*/

CControllerFrontView::CControllerFrontView(osg::Camera *camera, float ratio) : CControllerSideView(camera, ratio)
{
	updateViewMatrix();
}

void CControllerFrontView::updateViewMatrix()
{
	camera->setViewMatrixAsLookAt(osg::Vec3(-x, -y, 1), osg::Vec3(-x, -y, 0), osg::Vec3(0, 1, 0));
}





/*
**
** Back
**
*/

CControllerBackView::CControllerBackView(osg::Camera *camera, float ratio) : CControllerSideView(camera, ratio)
{
	updateViewMatrix();
}

void CControllerBackView::updateViewMatrix()
{
	camera->setViewMatrixAsLookAt(osg::Vec3(x, -y, -1), osg::Vec3(x, -y, 0), osg::Vec3(0, 1, 0));
}
